Wstęp

Na zbiorze danych clustering_R3.csv przetestujemy jeszcze inne dwie metody klasteryzacji. W zbiorze znajdują się trzy kolumny liczb, które można przedstawić punktami na układzie współrzędnych.

dane_oryg <- read.csv("../../clustering_R3.csv")
head(dane_oryg)
##         X1       X2        X3
## 1 152.0780 197.9104 1.0957074
## 2 145.5205 202.1280 2.7388257
## 3 155.4413 198.1227 0.6404565
## 4 150.3891 206.8084 0.6864856
## 5 149.8632 205.4175 3.5558137
## 6 142.4108 208.9581 3.3838489
tail(dane_oryg)
##            X1        X2        X3
## 995  357.6517 112.91781 17.151268
## 996  327.0585  95.04242  5.819534
## 997  379.8715 118.62044 13.329955
## 998  386.3637  97.26560  8.635707
## 999  331.2692 100.62328  6.228514
## 1000 359.1198  99.95350 20.211148
plot_ly(x=dane_oryg$X1, y=dane_oryg$X2, z=dane_oryg$X3, type="scatter3d", mode="markers", size=1)
plot(dane_oryg)

Wymiary są bardzo różnego zasięgu, więc przed pracą należałoby je przeskalować.

dane <- dane_oryg
for (x in 1:3)
  dane[,x] <- scale(dane[,x])
head(dane)
##           X1       X2         X3
## 1 -0.7624251 1.024041 -0.9093166
## 2 -0.8191723 1.124547 -0.5665699
## 3 -0.7333199 1.029100 -1.0042798
## 4 -0.7770403 1.236079 -0.9946783
## 5 -0.7815912 1.202935 -0.3961501
## 6 -0.8460827 1.287306 -0.4320212
tail(dane)
##             X1         X2         X3
## 995  1.0165666 -1.0013201 2.43979683
## 996  0.7518192 -1.4272881 0.07605107
## 997  1.2088514 -0.8654271 1.64268923
## 998  1.2650339 -1.3743100 0.66349137
## 999  0.7882578 -1.2942968 0.16136251
## 1000 1.0292707 -1.3102576 3.07807325

Funkcja do wykresów

Do przedstawienia wyników klasteryzacji definiuję sobie funkcję:

wykres <- function(da, q) {
  kolory <- unique(q)
  rbow <- rainbow(length(kolory))
  if (0 %in% kolory){
    q[q==0] <- max(kolory)+1
    rbow <- c(rainbow(length(kolory)-1), "#000000FF")
  }
  fig <- plot_ly(x=da$X1, y=da$X2, z=da$X3,
                 color=q, colors=rbow,
                 type="scatter3d", mode="markers",
                 size=1)
  fig
}

DBSCAN

W przeciwieństwie do metod z pracy domowej nr 5 oraz tej, która jeszcze się pojawi, DBSCAN samowolnie wyznacza liczbę klastrów do podzielenia danych. Problemem może jednak być prawidłowy dobór parametrów ε i minPts. Udało mi się te wartości samodzielnie dobrać w taki sposób, żeby podział na klastry wyglądał możliwie “naturalnie”, ale najpierw przedstawię sposób estymacji proponowany przez, ekhem, fachowców.

Parametr minPts powinien być większy od liczby wymiarów o co najmniej 1, zatem w tym wypadku minimum wynosi 4.

Epsilon należy wyznaczyć bardziej doświadczalnie. Proponowaną metodą jest przedstawienie na wykresie odległości każdego punktu od k-tego najbliższego sąsiada, posortowanych od największej do najmniejszej, gdzie k = minPts-1. Optymalna wartość epsilona zostaje wyznaczona w miejscu “zgięcia łokcia”.

Do obliczenia k najbliższych sąsiadów (co jest tutaj czymś innym, niż dobrze nam znany model ML) używam pakietu FNN.

knn_3 <- get.knn(dane, k=3)
plot(1:1000, sort(knn_3[[2]][,3], decreasing=T), xlab="3NN", ylab="dystans")

Tym razem pozwolę sobie przyjąć miejsce zgięcia na oko, bez obliczeń obecnych poprzednim razem. Tym samym biorę ε = 0,25.

Niżej wynik klasteryzacji, obliczony na skalowanych danych, ale nałożony już na oryginalne. Czarne punkty nie zostały przyporządkowane do żadnego skupienia:

dbs <- dbscan(dane, 0.25, 4)
clu0 <- dbs$cluster
wykres(dane_oryg, clu0)

Parametry, które udało mi się ręcznie ustalić jako te optymalne, to tymczasem ε = 0,4 i minPts = 3.

dbs2 <- dbscan(dane, 0.4, 3)
clu1 <- dbs2$cluster
wykres(dane_oryg, clu1)

GMM

Tutaj najpierw wybieram liczbę klastrów. Tak jak dwa tygodnie temu, zastosuję fviz_nbclust z pakietu factoextra.

fviz_nbclust(dane, kmeans, method="silhouette")

Wskazaną liczbą klastrów jest 2. GMM nie przyporządkowuje od razu punktów do klastrów, a jedynie wyznacza prawdopodobieństwo, z jakim punkty należą do każdego z nich. Wciąż, można na podstawie tego szybko uzyskać optymalny podział.

gmm <- GMM(dane, gaussian_comps=2)
clu2 <- apply(gmm$Log_likelihood, 1, which.max)
wykres(dane_oryg, clu2)

Porównanie wyników

Do porównania jakości klasteryzacji zastosuję współczynnik Silhouette i indeks Daviesa–Bouldina. W skrócie, współczynnik Silhouette daje informację, na ile podobne obserwacje są do swoich klastrów w porównaniu do innych. Miara przyjmuje wartości od -1 do 1, gdzie im wyższa wartość, tym mocniejsze przywiązanie obserwacji do jej skupienia. Indeks Daviesa-Bouldina natomiast ocenia klasteryzację na podstawie wartości i cech wywodzących się ze zbioru danych.

Jako reprezentację wyników DBSCAN wykorzystam podział dokonany z ręcznymi parametrami (0,4 i 3).

Współczynniki Silhouette

staty1 <- cluster.stats(dist(dane), clu1)
staty2 <- cluster.stats(dist(dane), clu2)
# współczynniki dla klastrów i średni współczynnik dla całego podziału
staty1$clus.avg.silwidths
##         1         2         3         4 
## 0.7993158 0.8079347 0.3178257 0.3533559
staty1$avg.silwidth
## [1] 0.5700917
staty2$clus.avg.silwidths
##         1         2 
## 0.5267710 0.7807689
staty2$avg.silwidth
## [1] 0.65377

Indeksy Daviesa-Bouldina

db1 <- index.DB(dane, clu1)
db2 <- index.DB(dane, clu2)
db1$DB
## [1] 1.065152
db2$DB
## [1] 0.6145745

Wyniki są dosyć nieoczywiste. Metoda GMM daje średnio lepszy współczynnik Silhouette, ale o dużo więcej wyższy indeks Daviesa-Bouldina osiągnęła metoda DBSCAN. Co więcej, biorąc pod uwagę mniejsze zautomatyzowanie DBSCAN, czyli że użytkownik sam wybiera parametry i może nią osiągnąć bardziej “pożądane” wyniki, to tę metodę wolałbym wyróżnić.

Oświadczenie

Oświadczam, że niniejsza praca stanowiąca podstawę do uznania osiągnięcia efektów uczenia się z przedmiotu Wstęp do uczenia maszynowego została wykonana przeze mnie samodzielnie.